package edu.cmu.mism.dgjava.views;

import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;

import edu.cmu.mism.dgjava.data.models.option.AmericanType;
import edu.cmu.mism.dgjava.data.models.option.Derivitives;
import edu.cmu.mism.dgjava.data.models.option.Equity;
import edu.cmu.mism.dgjava.data.models.option.EuropeanType;
import edu.cmu.mism.dgjava.data.models.option.PricingModel;
import edu.cmu.mism.dgjava.graphs.DGChartDiagram;
import edu.cmu.mism.dgjava.jobs.IGraphCalculationCallback;
import edu.cmu.mism.dgjava.jobs.OptionCalculationJob;
import edu.cmu.mism.dgjava.jobs.OptionCalculationJobManager;
import edu.cmu.mism.dgjava.utils.IDGConstants;
import edu.cmu.mism.dgjava.utils.SWTUtils;

/**
 * @author Chris(Tian) Qiu
 * 
 */
public class GraphicResults extends ViewPart {

	public static final String ID = "DGJava.view.graphicResults"; //$NON-NLS-1$
	private Label _verticalLbl;
	private Combo _verticalComb;
	private Label _horizontalLbl;
	private Combo _horizontalComb;

	private Label _minXValueLbl;
	private Text _minXValueTxt;
	private Label _minXValueMessageLbl;
	
	
	private Label _maxXValueLbl;
	private Text _maxXValueTxt;
	private Label _maxXValueMessageLbl;


	private Button _drawBtn;

	private ModifyListener _validator = new ModifyListener() {

		@Override
		public void modifyText(ModifyEvent e) {
			validate();
		}
	};
	private DGChartDiagram chartDiagram;

	private IGraphCalculationCallback callback = new IGraphCalculationCallback() {

		@Override
		public void updateDataSet(double[] x, double[] y, String xLabel,
				String yLabel) {
			if (chartDiagram != null) {
				chartDiagram.updateDataSet(x, y, xLabel, yLabel);
				chartDiagram.redraw();
			}
		}
	};

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets
	 * .Composite)
	 */
	@Override
	public void createPartControl(Composite parent) {
		Composite topContainer = SWTUtils.createComposite(parent, 3, -1, 1);

		// initiate parameter group
		createParameterGroup(topContainer);

		// seperator
		SWTUtils.createSeparator(topContainer, 2);

		// create draw graphic button
		createDrawBtn(topContainer);

		createGraphArea(topContainer);
		
		updateDefaultMinMaxValue();

		// hook up modify listener
		hookupModifyListener();
		
		validate();
	}

	private void createGraphArea(Composite topContainer) {
		this.chartDiagram = new DGChartDiagram(topContainer, SWT.NONE);
		GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
		gd.horizontalSpan = 2;
		this.chartDiagram.setLayoutData(gd);
	}

	/**
	 * Hook up modify listener to observe modification event and validate user's
	 * input
	 */
	private void hookupModifyListener() {
		this._maxXValueTxt.addModifyListener(this._validator);
		this._minXValueTxt.addModifyListener(this._validator);
	}

	/**
	 * @param topContainer
	 */
	private void createDrawBtn(Composite topContainer) {
		this._drawBtn = SWTUtils.createPushButton(topContainer,
				Messages.GraphicResults_button_draw);
		GridData btnGd = new GridData(SWT.FILL, SWT.NONE, false, false);
		btnGd.horizontalSpan = 3;
		btnGd.horizontalAlignment = GridData.CENTER;
		this._drawBtn.setLayoutData(btnGd);

		this._drawBtn.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				int xKey = _horizontalComb.getSelectionIndex();
				int yKey = _verticalComb.getSelectionIndex();
				double xMin = Double
						.parseDouble(_minXValueTxt.getText().trim());
				double xMax = Double
						.parseDouble(_maxXValueTxt.getText().trim());

				OptionCalculationJobManager
						.getInstance()
						.getLastCalculation()
						.calculateGraphResults(xKey, xMin, xMax, yKey, callback);

			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// TODO
			}
		});

	}

	/**
	 * @param container
	 */
	private void createParameterGroup(Composite container) {
		// vertical axis
		this._verticalLbl = SWTUtils.createLabel(container,
				Messages.GraphicResults_label_vertical_axis, 1);
		GridData labelGd = new GridData(GridData.HORIZONTAL_ALIGN_END);
		this._verticalLbl.setLayoutData(labelGd);
		this._verticalComb = SWTUtils.createCombo(container,
				IDGConstants.OPTION_GRAPHICS_VERTICAL_AXIS, 2);
		GridData hGd = new GridData(SWT.FILL, -1, true, false);
		hGd.horizontalSpan = 2;
		this._verticalComb.setLayoutData(hGd);
		this._verticalComb.select(0);

		// horizontal axis
		this._horizontalLbl = SWTUtils.createLabel(container,
				Messages.GraphicResults_label_horizontal_axis, 1);
		labelGd = new GridData(GridData.HORIZONTAL_ALIGN_END);
		this._horizontalLbl.setLayoutData(labelGd);
		this._horizontalComb = SWTUtils.createCombo(container,
				IDGConstants.OPTION_GRAPHICS_HORIZONTAL_AXIS, 2);
		hGd = new GridData(SWT.FILL, -1, true, false);
		hGd.horizontalSpan = 2;
		this._horizontalComb.setLayoutData(hGd);
		this._horizontalComb.select(0);
		this._horizontalComb.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				updateDefaultMinMaxValue();
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// TODO Auto-generated method stub

			}
		});

		// minimum x value
		this._minXValueLbl = SWTUtils.createLabel(container,
				Messages.GraphicResults_label_minimumX, 1);
		labelGd = new GridData(GridData.HORIZONTAL_ALIGN_END);
		this._minXValueLbl.setLayoutData(labelGd);
		this._minXValueTxt = SWTUtils.createTextBox(container, "20"); //$NON-NLS-1$
		hGd = new GridData(SWT.FILL, -1, true, false);
		this._minXValueTxt.setLayoutData(hGd);
		this._minXValueMessageLbl = SWTUtils.createLabel(container, "", 1);
		this._minXValueMessageLbl.setImage(JFaceResources
				.getImage(IDGConstants.DLG_IMG_TITLE_ERROR));

		// maximum x value
		this._maxXValueLbl = SWTUtils.createLabel(container,
				Messages.GraphicResults_label_maximumX, 1);
		labelGd = new GridData(GridData.HORIZONTAL_ALIGN_END);
		this._maxXValueLbl.setLayoutData(labelGd);
		this._maxXValueTxt = SWTUtils.createTextBox(container, "100"); //$NON-NLS-1$
		hGd = new GridData(SWT.FILL, -1, true, false);
		this._maxXValueTxt.setLayoutData(hGd);
		this._maxXValueMessageLbl = SWTUtils.createLabel(container, "", 1);
		this._maxXValueMessageLbl.setImage(JFaceResources
				.getImage(IDGConstants.DLG_IMG_TITLE_ERROR));
		
	}

	public void updateDefaultMinMaxValue() {
		OptionCalculationJob job = OptionCalculationJobManager.getInstance()
				.getLastCalculation();
		if (job == null) {
			this._drawBtn.setEnabled(false);
			this._horizontalComb.setEnabled(false);
			this._verticalComb.setEnabled(false);
		} else {
			this._drawBtn.setEnabled(true);
			this._horizontalComb.setEnabled(true);
			this._verticalComb.setEnabled(true);
			double minValue = 0.00;
			double maxValue = 0.00;
			double rawValue = 0.00;
			switch (this._horizontalComb.getSelectionIndex()) {
			case 0:
				// Asset Price
				rawValue = ((Equity) job.getUnderlyingTypeMode())
						.getStockPrice();
				minValue = rawValue - 10;
				maxValue = rawValue + 10;
				break;
			case 1:
				// Strike Price
				rawValue = ((PricingModel) job.getPricingModel())
						.getExercisePrice();
				minValue = rawValue - 10;
				maxValue = rawValue + 10;
				break;
			case 2:
				// Risk-free Rate
				rawValue = ((Derivitives) job.getUnderlyingTypeMode())
						.getRiskFreeRate();
				minValue = rawValue - 1;
				maxValue = rawValue + 1;
				break;
			case 3:
				// Time to Exercise
				if (job.getPricingModel() instanceof AmericanType) {
					rawValue = ((AmericanType) job.getPricingModel())
							.getTimeToExpire();
				} else if (job.getPricingModel() instanceof EuropeanType) {
					rawValue = ((EuropeanType) job.getPricingModel())
							.getTimeToExercise();
				}
				minValue = rawValue - 0.01;
				maxValue = rawValue + 0.01;
				break;
			case 4:
				// Volatility
				rawValue = ((Derivitives) job.getUnderlyingTypeMode())
						.getVolatility();
				minValue = rawValue - 20;
				maxValue = rawValue + 20;
				break;
			default:
				break;
			}

			this._minXValueTxt.setText("" + minValue);
			this._maxXValueTxt.setText("" + maxValue);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
	 */
	@Override
	public void setFocus() {
		// TODO Auto-generated method stub

	}

	/**
	 * Validate User's Input
	 */
	public void validate() {
		boolean valid = true;
		if (this._minXValueTxt.getText() == null
				|| this._minXValueTxt.getText().length() == 0) {
			this._minXValueMessageLbl
					.setToolTipText("Minimum value can not be empty");
			this._minXValueMessageLbl.setVisible(true);
			valid = false;
		} else {
			try {
				Double.valueOf(this._minXValueTxt.getText()
						.trim());
				this._minXValueMessageLbl.setToolTipText(null);
				this._minXValueMessageLbl.setVisible(false);
				
			} catch (NumberFormatException e) {
				this._minXValueMessageLbl
						.setToolTipText("Minimum value must a valid decimal number");
				this._minXValueMessageLbl.setVisible(true);
				valid = false;
			}
		}
		if (this._maxXValueTxt.getText() == null
				|| this._maxXValueTxt.getText().length() == 0) {
			this._maxXValueMessageLbl
					.setToolTipText("Maximum value can not be empty");
			this._maxXValueMessageLbl.setVisible(true);
			valid = false;

		} else {
			try {
				Double.valueOf(this._maxXValueTxt.getText()
						.trim());
				this._maxXValueMessageLbl.setToolTipText(null);
				this._maxXValueMessageLbl.setVisible(false);
				
			} catch (NumberFormatException e) {
				this._maxXValueMessageLbl
						.setToolTipText("Maximum value must be a valid decimal number");
				this._maxXValueMessageLbl.setVisible(true);
				valid = false;

			}
		}
		
		this._drawBtn.setEnabled(valid
				&& (OptionCalculationJobManager.getInstance()
						.getLastCalculation() != null));
	}

}
